package com.dstukalov.videoconverter.muxer;

import android.util.SparseIntArray;

import androidx.annotation.NonNull;

import org.mp4parser.boxes.iso14496.part1.objectdescriptors.AudioSpecificConfig;
import org.mp4parser.boxes.iso14496.part1.objectdescriptors.DecoderConfigDescriptor;
import org.mp4parser.boxes.iso14496.part1.objectdescriptors.ESDescriptor;
import org.mp4parser.boxes.iso14496.part1.objectdescriptors.SLConfigDescriptor;
import org.mp4parser.boxes.iso14496.part12.SampleDescriptionBox;
import org.mp4parser.boxes.iso14496.part14.ESDescriptorBox;
import org.mp4parser.boxes.sampleentry.AudioSampleEntry;
import org.mp4parser.streaming.extensions.DefaultSampleFlagsTrackExtension;
import org.mp4parser.streaming.input.AbstractStreamingTrack;
import org.mp4parser.streaming.input.StreamingSampleImpl;

import java.io.IOException;
import java.nio.ByteBuffer;

public class AacTrack extends AbstractStreamingTrack {

    private static final SparseIntArray SAMPLING_FREQUENCY_INDEX_MAP = new SparseIntArray();
    static {
        SAMPLING_FREQUENCY_INDEX_MAP.put(96000, 0);
        SAMPLING_FREQUENCY_INDEX_MAP.put(88200, 1);
        SAMPLING_FREQUENCY_INDEX_MAP.put(64000, 2);
        SAMPLING_FREQUENCY_INDEX_MAP.put(48000, 3);
        SAMPLING_FREQUENCY_INDEX_MAP.put(44100, 4);
        SAMPLING_FREQUENCY_INDEX_MAP.put(32000, 5);
        SAMPLING_FREQUENCY_INDEX_MAP.put(24000, 6);
        SAMPLING_FREQUENCY_INDEX_MAP.put(22050, 7);
        SAMPLING_FREQUENCY_INDEX_MAP.put(16000, 8);
        SAMPLING_FREQUENCY_INDEX_MAP.put(12000, 9);
        SAMPLING_FREQUENCY_INDEX_MAP.put(11025, 10);
        SAMPLING_FREQUENCY_INDEX_MAP.put(8000, 11);
    }

    private final SampleDescriptionBox stsd;
    private final int sampleRate;

    AacTrack(long avgBitrate, long maxBitrate, int sampleRate, int channelCount, int aacProfile) {
        this.sampleRate = sampleRate;

        final DefaultSampleFlagsTrackExtension defaultSampleFlagsTrackExtension = new DefaultSampleFlagsTrackExtension();
        defaultSampleFlagsTrackExtension.setIsLeading(2);
        defaultSampleFlagsTrackExtension.setSampleDependsOn(2);
        defaultSampleFlagsTrackExtension.setSampleIsDependedOn(2);
        defaultSampleFlagsTrackExtension.setSampleHasRedundancy(2);
        defaultSampleFlagsTrackExtension.setSampleIsNonSyncSample(false);
        this.addTrackExtension(defaultSampleFlagsTrackExtension);

        stsd = new SampleDescriptionBox();
        final AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a");
        if (channelCount == 7) {
            audioSampleEntry.setChannelCount(8);
        } else {
            audioSampleEntry.setChannelCount(channelCount);
        }
        audioSampleEntry.setSampleRate(sampleRate);
        audioSampleEntry.setDataReferenceIndex(1);
        audioSampleEntry.setSampleSize(16);


        final ESDescriptorBox esds = new ESDescriptorBox();
        final ESDescriptor descriptor = new ESDescriptor();
        descriptor.setEsId(0);

        final SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor();
        slConfigDescriptor.setPredefined(2);
        descriptor.setSlConfigDescriptor(slConfigDescriptor);

        final DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor();
        decoderConfigDescriptor.setObjectTypeIndication(0x40 /*Audio ISO/IEC 14496-3*/);
        decoderConfigDescriptor.setStreamType(5 /*audio stream*/);
        decoderConfigDescriptor.setBufferSizeDB(1536);
        decoderConfigDescriptor.setMaxBitRate(maxBitrate);
        decoderConfigDescriptor.setAvgBitRate(avgBitrate);

        final AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
        audioSpecificConfig.setOriginalAudioObjectType(aacProfile);
        audioSpecificConfig.setSamplingFrequencyIndex(SAMPLING_FREQUENCY_INDEX_MAP.get(sampleRate));
        audioSpecificConfig.setChannelConfiguration(channelCount);
        decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);

        descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor);

        esds.setEsDescriptor(descriptor);

        audioSampleEntry.addBox(esds);
        stsd.addBox(audioSampleEntry);
    }

    public long getTimescale() {
        return sampleRate;
    }

    public String getHandler() {
        return "soun";
    }

    public String getLanguage() {
        return "\u0060\u0060\u0060"; // 0 in Iso639
    }

    public synchronized SampleDescriptionBox getSampleDescriptionBox() {
        return stsd;
    }

    public void close() {
    }

    void processSample(@NonNull ByteBuffer frame) throws IOException {
        sampleSink.acceptSample(new StreamingSampleImpl(frame, 1024), this);
    }
}
